package com.xing.okhttp;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import okhttp3.*;

import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class HttpClientManager implements IHttpClientManager {

    private OkHttpClient httpClient;

    private int defalutConnectTimeout = 3000;
    private int defaultReadTimeout = 2000;
    private int defaultWriteTimeout = 2000;

    private static final String GZIP = "gzip";

    private boolean usePrivateSSL;
    private boolean useSSL;

    private SSLContext sslContext;
    private X509TrustManager x509TrustManager;

    private String proxyHost;
    private Integer proxyPort;
    private String proxyUser;
    private String proxyPass;

    private Authenticator authenticator;

    private Proxy proxy;


    HttpClientManager(HttpClientManager.Builder builder) {

        usePrivateSSL = builder.usePrivateSSL;
        useSSL = builder.useSSL;
        proxyHost = builder.proxyHost;
        proxyPort = builder.proxyPort;
        proxyUser = builder.proxyUser;
        proxyPass = builder.proxyPass;

        createSslContext();

        if ((proxyHost != null && proxyHost.length() > 0) && (proxyPort != null)) {

            proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));

            authenticator = getProxyAuthenticator(proxyUser, proxyPass);

        }

        createHttpClient();

    }


    private void createSslContext() {
        try {
            x509TrustManager = SSLHelper.trustManager();
            sslContext = SSLHelper.createSSLContext(usePrivateSSL, useSSL, x509TrustManager);
        } catch (Throwable throwable) {
        }
    }

    private void createHttpClient() {

        OkHttpClient.Builder okBuilder = new OkHttpClient.Builder()
                .connectTimeout(defalutConnectTimeout, TimeUnit.MILLISECONDS)
                .readTimeout(defaultReadTimeout, TimeUnit.MILLISECONDS)
                .writeTimeout(defaultWriteTimeout, TimeUnit.MILLISECONDS)
                ;

        if(proxy != null) {
            okBuilder.proxy(proxy);
        }

        if(sslContext != null) {
            okBuilder.sslSocketFactory(sslContext.getSocketFactory(), x509TrustManager);
        }

        if(authenticator != null) {
            okBuilder.proxyAuthenticator(authenticator);
        }


        httpClient = okBuilder.build();
    }


    @Override
    public Response syncPost(URL url, String encoding, Object postBody) throws Exception {
        byte[] data = HttpClientHelper.writeData(encoding, postBody);
        Request request = createPostRequest(url, encoding, data);
        Response response = httpClient.newCall(request).execute();
        return response;
    }

    @Override
    public Response syncPost(String protocol, String host, int port, String uri, String encoding, Object postBody) throws Exception {
        URL url = new URL(protocol, host, port, uri);
        return syncPost(url, encoding, postBody);
    }

    @Override
    public Response syncPost(URL url, String encoding, byte[] data) throws Exception {
        Request request = createPostRequest(url, encoding, data);
        Response response = httpClient.newCall(request).execute();
        return response;
    }

    @Override
    public Response syncGet(String protocol, String host, int port, String uri, String encoding) throws Exception {
        URL url = new URL(protocol, host, port, uri);
        Request request = createGetRequest(url, encoding);
        return httpClient.newCall(request).execute();
    }

    private Request createPostRequest(URL url, String encoding, byte data[]) {
        Request.Builder builder = new Request.Builder()
                .url(url)
                .post(RequestBody.create(MediaType.parse("application/octet-stream"), data))
                .addHeader("Connection", "Keep-Alive")
                .addHeader("ACCEPT-ENCODING", GZIP);
        if(encoding!=null && encoding.length() > 0){
            builder.addHeader("CONTENT-ENCODING", encoding);
        }
        return builder.build();
    }

    private Request createGetRequest(URL url, String encoding) {
        Request.Builder builder = new Request.Builder()
                .url(url)
                .get()
                .addHeader("Connection", "Keep-Alive")
                //.addHeader("ACCEPT-ENCODING", GZIP);
        ;
        if(encoding!=null && encoding.length() > 0){
            builder.addHeader("CONTENT-ENCODING", encoding);
        }
        return builder.build();
    }


    private Authenticator getProxyAuthenticator(final String proxyUser, final String proxyPass) {
        if (proxyUser != null && proxyPass != null) {
            return new Authenticator() {
                @Override
                public Request authenticate(Route route, Response response) throws IOException {
                    String credential = Credentials.basic(proxyUser, proxyPass);
                    return response.request().newBuilder()
                            .header("Proxy-Authorization", credential)
                            .build();
                }
            };
        } else {
            return null;
        }
    }


    /**
     * 转化响应内容为Map格式
     *
     * @param responseBody
     * @return
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> getResponseMap(String responseBody) throws Exception {
        Type type = new TypeReference<Map<String, Object>>() {
        }.getType();
        Object response = JSON.parseObject(responseBody, type);
        return Map.class.cast(response);
    }


    public static final class Builder {

        private boolean useSSL;

        private boolean usePrivateSSL;

        private String proxyHost;

        private Integer proxyPort;

        private String proxyUser;

        private String proxyPass;

        public Builder() {
        }

        public HttpClientManager.Builder useSSL(boolean useSsl) {
            this.useSSL = useSsl;
            return this;
        }

        public HttpClientManager.Builder usePrivateSSL(boolean usePrivateSSL) {
            this.usePrivateSSL = usePrivateSSL;
            return this;
        }

        public HttpClientManager.Builder proxyHost(String proxyHost) {
            this.proxyHost = proxyHost;
            return this;
        }

        public HttpClientManager.Builder proxyPort(Integer proxyPort) {
            this.proxyPort = proxyPort;
            return this;
        }

        public HttpClientManager.Builder proxyUser(String proxyUser) {
            this.proxyUser = proxyUser;
            return this;
        }

        public HttpClientManager.Builder proxyPass(String proxyPass) {
            this.proxyPass = proxyPass;
            return this;
        }

        public HttpClientManager build() {
            return new HttpClientManager(this);
        }
    }
    
}
